# -*- coding: utf-8 -*-

"""
 (c) 2023 - Copyright CTyunOS Inc

 Authors:
   youyifeng <youyf2@chinatelecom.cn>

"""
import json
import logging
import os
from optparse import OptionParser

from cve_ease import activate_session, Scraper
from cve_ease.helper import check_product
from cve_ease.helper import one_instance_check
from cve_ease.helper import parseRPM
from cve_ease.models import CVRFXML, CVRF, PRODUCT

logger = logging.getLogger('cve-ease')


def get_usage_str(usage):
    return usage + "\n(Specify the --help global option for a list of other help options)"


def purge_output():
    if os.path.exists('output'):
        import shutil
        shutil.rmtree('output')


def cvrf_makecache(options, session):
    scraper = Scraper(options.trytimes)
    for (index, no, left, cvrf) in scraper.scrapyCVRF():
        cvrf_xml_handler = CVRFXML(cvrf)
        cvrf = CVRF()
        cvrf.securityNoticeNo = cvrf_xml_handler.node_get_securityNoticeNo()
        cvrf.affectedComponent = cvrf_xml_handler.node_get_affectedComponent()
        cvrf.cveId = ";".join(cvrf_xml_handler.node_get_cveId()) + ';'
        cvrf.affectedProduct = ";".join(cvrf_xml_handler.node_get_affectedProduct()) + ';'
        pkg_dict = cvrf_xml_handler.node_get_packageName()
        cvrf.packageName = str(pkg_dict)

        for product in pkg_dict:
            for persrc in pkg_dict[product]["src"]:
                name, _, _, _, _ = parseRPM(persrc)
                p = PRODUCT()
                p.productId = product
                p.srcfilename = persrc
                p.packageName = name
                p.securityNoticeNo = cvrf.securityNoticeNo
                p.packages = json.dumps(pkg_dict[product])
                session.add(p)
                session.commit()
                if options.verbose:
                    print("product: %s   src: %s  name: %s" % (p.productId, p.srcfilename, p.packageName))
                    print(json.dumps(p.packages, indent=4))
        session.add(cvrf)
        session.commit()
        print("[%5d/%-5d] Cache OK! %s" % (no, left, os.path.basename(index)))


def handle_cvrf(gconfig, db_session, args):
    """[info] OpenEuler CVRF info"""

    # one instance
    lock = one_instance_check(gconfig.LOCK_FILE_PATH)
    if lock:
        raise Exception(f"Another cve-ease already running")

    usage = "usage: %prog cvrf <options>"
    parser = OptionParser(usage=get_usage_str(usage))

    parser.add_option('--product', dest='product', default='openEuler-22.03-LTS-SP1',
                      help='specific product')
    parser.add_option('-m', '--makecache', dest='makecache', action='store_true', default=False,
                      help='get cvrf cache, write to database')
    parser.add_option('-p', '--purge', dest='purge', action='store_true', default=False,
                      help='purge old download')
    parser.add_option('-r', '--rawdata', dest='raw', action='store_true', default=False,
                      help='get cvrf cache and print raw data without write db')
    parser.add_option('-l', '--list', dest='list', action='store_true', default=False,
                      help='list all cvrf info')
    parser.add_option('-t', '--total', dest='total', action='store_true', default=False,
                      help='get cvrf info statistics')
    parser.add_option('-c', '--check', dest='check', action='store_true', default=False,
                      help='check current os with cvrf to find unfixed cve')
    parser.add_option('--try', dest='trytimes', default=10,
                      help='retry times')
    parser.add_option("-j", "--job", dest='job', default=os.cpu_count(), type=int,
                      help="multi process setting")
    parser.add_option('-v', '--verbose', dest='verbose', action='store_true', default=False,
                      help='show verbose output')

    (options, args) = parser.parse_args(args)

    session = activate_session(db_session, gconfig)
    if gconfig.debug:
        print(" * active sql session")

    if options.makecache:
        from cve_ease.helper import get_timestamp
        import multiprocessing
        begin_time = get_timestamp()
        scraper = Scraper()
        if options.purge:
            purge_output()
        os.makedirs('output', mode=0o777, exist_ok=True)

        cvrf_index = scraper.scrapy_CVRF_index()
        urls_with_index = []
        prefix = gconfig.CVRF_CACHE_PATH
        logger.debug(f" * store prefix path {prefix}")
        os.makedirs(prefix, exist_ok=True)
        total = len(cvrf_index)
        for i, url in enumerate(cvrf_index):
            url = url.strip()
            urls_with_index.append((i + 1, total, prefix, url))

        pool = multiprocessing.Pool(options.job)
        pool.imap_unordered(scraper.process_per_cvrf, urls_with_index)
        pool.close()
        pool.join()

        print()
        print(" * checking...")
        print()
        for cvrf in cvrf_index:
            cvrf = cvrf.strip()
            file_path = os.path.join(prefix, cvrf)
            if not os.path.exists(file_path):
                print(f" error: Not found [{file_path}] ")
        print()

        print(" * write to database...")
        CVRF.__table__.drop(db_session.get_engine())
        CVRF.__table__.create(db_session.get_engine())
        for index, cvrf in enumerate(cvrf_index):
            cvrf = cvrf.strip()
            file_path = os.path.join(prefix, cvrf)
            if os.path.exists(file_path):
                with open(file_path, 'r') as cvrf_file:
                    cvrf_xml_handler = CVRFXML(cvrf_file.read())
                cvrf = CVRF()
                cvrf.securityNoticeNo = cvrf_xml_handler.node_get_securityNoticeNo()
                cvrf.affectedComponent = cvrf_xml_handler.node_get_affectedComponent()
                cvrf.cveId = ";".join(cvrf_xml_handler.node_get_cveId()) + ';'
                cvrf.cveThreat = ";".join(cvrf_xml_handler.node_get_cveThreat()) + ';'
                cvrf.affectedProduct = ";".join(cvrf_xml_handler.node_get_affectedProduct()) + ';'
                pkg_dict = cvrf_xml_handler.node_get_packageName()
                cvrf.packageName = str(pkg_dict)

                for product in pkg_dict:
                    for persrc in pkg_dict[product]["src"]:
                        name, _, _, _, _ = parseRPM(persrc)
                        p = PRODUCT()
                        p.productId = product
                        p.srcfilename = persrc
                        p.packageName = name
                        p.securityNoticeNo = cvrf.securityNoticeNo
                        p.packages = json.dumps(pkg_dict[product])
                        session.add(p)
                        session.commit()
                        if options.verbose:
                            print("product: %s   src: %s  name: %s" % (p.productId, p.srcfilename, p.packageName))
                            print(json.dumps(p.packages, indent=4))
                session.add(cvrf)
                session.commit()
                print(f" [{index}/{total}] write {file_path} to database done!")
        end_time = get_timestamp()
        print(f"cvrf makecache done! {begin_time} - {end_time}")
    elif options.raw:
        scraper = Scraper()
        for cvrf in scraper.scrapy_CVRF_index():
            print("%s" % os.path.join("https://repo.openeuler.org/security/data/cvrf", cvrf))

    elif options.check:
        productId = check_product(gconfig, options)
        print(" * Using product:", productId)

        try:
            import rpm
        except Exception as e:
            print("import rpm failed!", str(e))
            exit(1)
        ts = rpm.TransactionSet()
        rpm_hdr_iter = ts.dbMatch()

        for rpm_hdr in rpm_hdr_iter:
            srpm = rpm_hdr[rpm.RPMTAG_SOURCERPM]
            name, _, _, _, _ = parseRPM(srpm)
            from sqlalchemy import and_
            srpm_objs = session.query(PRODUCT).filter(
                and_(PRODUCT.productId == options.product, PRODUCT.packageName == name)
            ).all()

            if len(srpm_objs) > 0:
                # show installed package info
                print(
                    "[NO] %-30s %-20s %-5s %-20s %-15s %s(%s)" % (
                        rpm_hdr[rpm.RPMTAG_NAME],
                        rpm_hdr[rpm.RPMTAG_VERSION],
                        rpm_hdr[rpm.RPMTAG_EPOCH] or '0',
                        rpm_hdr[rpm.RPMTAG_RELEASE],
                        rpm_hdr[rpm.RPMTAG_ARCH],
                        srpm,
                        name
                    )
                )
            else:
                if options.verbose:
                    print(
                        "[OK] %-30s %-20s %-5s %-20s %-15s %s(%s)" % (
                            rpm_hdr[rpm.RPMTAG_NAME],
                            rpm_hdr[rpm.RPMTAG_VERSION],
                            rpm_hdr[rpm.RPMTAG_EPOCH] or '0',
                            rpm_hdr[rpm.RPMTAG_RELEASE],
                            rpm_hdr[rpm.RPMTAG_ARCH],
                            srpm,
                            name
                        )
                    )

            for srpm in srpm_objs:
                # show candidate package info
                print(" * find candidate: ", srpm.srcfilename)
                # need compare version
    elif options.total:
        total = session.query(CVRF).count()
        print("cvrf total record :", total)
        if 0 == total:
            print(" WARNNING: no cvrf cache found! you should run 'cve-ease cvrf -m' to cache it.")
    elif options.list:
        cvrfDatabaseList = session.query(CVRF).all()
        if 0 == len(cvrfDatabaseList):
            print(" WARNNING: no cvrf cache found! you should run 'cve-ease cvrf -m' to cache it.")
            return
        for cvrf in cvrfDatabaseList:
            cvrf.format_output()
    else:
        parser.print_help()
